package com.p2p.front.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.activemq.command.ActiveMQTopic;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.p2p.base.constents.InnerAccountConsts;
import com.p2p.base.model.Money;
import com.p2p.base.page.PageVo;
import com.p2p.base.service.ProducerService;
import com.p2p.base.util.DateUtil;
import com.p2p.front.dal.dao.TenderReturnDao;
import com.p2p.front.dal.dao.TransferDao;
import com.p2p.front.dal.model.LoanInfo;
import com.p2p.front.dal.model.Tender;
import com.p2p.front.dal.model.TenderReturn;
import com.p2p.front.dal.model.TenderReturn.ReturnType;
import com.p2p.front.enums.LoanRepayMethod;
import com.p2p.front.enums.TenderReturnStatus;
import com.p2p.front.enums.TenderStatus;
import com.p2p.front.result.TenderReturnResult;
import com.p2p.front.service.LoanInfoService;
import com.p2p.front.service.LoanVariationService;
import com.p2p.front.service.TenderReturnService;
import com.p2p.front.service.TenderService;
import com.p2p.front.service.TransferService;
import com.p2p.front.util.RateUtil;
import com.p2p.trans.client.enums.SubTransCodeEnum;
import com.p2p.trans.client.enums.TransCodeEnum;
import com.p2p.trans.client.facade.AccountQueryFacade;
import com.p2p.trans.client.facade.AccountTransFacade;
import com.p2p.trans.client.model.TransInfo;
import com.p2p.trans.client.result.AccountTransResult;
import com.p2p.trans.client.utils.AccountUtil;

/**
 * @author ruiqi
 *
 */
@Service("tenderReturnService")
public class TenderReturnServiceImpl implements TenderReturnService {

	protected final Logger logger = LoggerFactory.getLogger(TenderReturnServiceImpl.class);
	
	@Resource
	private TenderReturnDao tenderReturnDao;

	@Resource
	private LoanVariationService loanVariationService;

	@Resource
	private LoanInfoService loanInfoService;

	@Resource
	private ProducerService producerService;
	
	@Resource
	protected AccountTransFacade accountTransFacade;
	
	@Resource
	protected AccountQueryFacade accountQueryFacade;

	@Resource
	protected TransferDao transferDao;
	
	@Resource
	protected TransferService transferService;
	
	@Resource
	private TenderService tenderService;
	
	@Override
	public void create(TenderReturn tenderReturn) {
		tenderReturnDao.create(tenderReturn);
	}

	@Override
	public void create(Tender tender, LoanInfo loanInfo) {
		/*
		 * TenderReturn tenderReturn = new TenderReturn();
		 * 
		 * tenderReturn.setLid(tender.getLid());
		 * tenderReturn.setUid(tender.getUid());
		 * 
		 * tenderReturn.setTid(tender.getTid());
		 * tenderReturn.setPrincipal(tender.getAmount());
		 * 
		 * tenderReturn.setAmount(tender.getAmount());
		 */
		List<TenderReturn> list = createByRepayMethod(tender);
		for (TenderReturn tempReturn : list) {
			this.create(tempReturn);
		}
	}

	private List<TenderReturn> createByRepayMethod(Tender tender) {
		List<TenderReturn> list = new ArrayList<TenderReturn>();
		LoanInfo loanInfo = loanVariationService.getLoanInfoFromCache(tender
				.getLid());
		String str = tender.getLoanRepayMethod();
		LoanRepayMethod repayMethod = LoanRepayMethod.getEnumByName(str);
		// Calendar calendar = Calendar.getInstance();
		Date now = new Date();
		Integer isFlow = loanInfo.getIsFlow();
		if (repayMethod == LoanRepayMethod.m) {
			Money monthRepay = RateUtil
					.getMonthRepayAmountForPrincipalAndInterest(
							loanInfo.getReturnApr(), loanInfo.getDeadline(),
							tender.getAmount());
			Money monthPrincipal = new Money(tender.getAmount().getCent()
					/ loanInfo.getDeadline());
			Money tempAllRepay = new Money();
			for (int i = 1; i <= loanInfo.getDeadline(); i++) {
				TenderReturn tenderReturn = new TenderReturn();
				tenderReturn.setStatus(21);
				tenderReturn.setLid(tender.getLid());
				tenderReturn.setUid(tender.getUid());

				tenderReturn.setTid(tender.getTid());
				tenderReturn.setIsFlow(isFlow);

				tenderReturn.setAmount(monthRepay);

				tenderReturn.setPrincipal(monthPrincipal);

				tenderReturn.setNo(i);
				tenderReturn.setReturnType(ReturnType.NORMAL.getCode());
				Date time = DateUtil.addMonth(now, i);
				Date startTime = DateUtil.addMonth(now, i-1);
				tenderReturn.setTime(time);
				tenderReturn.setStartTime(startTime);
				if (i == loanInfo.getDeadline()) {
					tenderReturn.setPrincipal(new Money(tender.getAmount()
							.getCent() - tempAllRepay.getCent()));
				}
				tempAllRepay.add(monthPrincipal);
				list.add(tenderReturn);
			}
		} else if (repayMethod == LoanRepayMethod.i) {

			Money monthRepay = RateUtil.getMonthInterestForNormal(
					loanInfo.getApr(), loanInfo.getDeadline(),
					tender.getAmount());
			Money tempAllRepay = new Money();
			Money monthPrincipal = new Money();
			for (int i = 1; i <= loanInfo.getDeadline(); i++) {
				TenderReturn tenderReturn = new TenderReturn();
				tenderReturn.setStatus(21);
				tenderReturn.setLid(tender.getLid());
				tenderReturn.setUid(tender.getUid());

				tenderReturn.setTid(tender.getTid());
				tenderReturn.setIsFlow(isFlow);

				tenderReturn.setAmount(new Money(monthRepay.getCent()));

				tenderReturn.setPrincipal(monthPrincipal);

				tenderReturn.setNo(i);
				tenderReturn.setReturnType(ReturnType.NORMAL.getCode());
				Date time = DateUtil.addMonth(now, i);
				Date startTime = DateUtil.addMonth(now, i-1);
				tenderReturn.setTime(time);
				tenderReturn.setStartTime(startTime);
				
				if (i == loanInfo.getDeadline()) {
					tenderReturn.setPrincipal(new Money(tender.getAmount()
							.getCent() - tempAllRepay.getCent()));
					tenderReturn.getAmount().add(tenderReturn.getPrincipal());
				}
				tempAllRepay.add(monthPrincipal);
				list.add(tenderReturn);
			}
		} else if (repayMethod == LoanRepayMethod.e) {
			Money allInterestRepay = RateUtil.getAllInterestForNormal(
					loanInfo.getApr(), loanInfo.getDeadline(),
					tender.getAmount());

			Money tempAllRepay = new Money(tender.getAmount().getCent()
					+ allInterestRepay.getCent());
			Money monthPrincipal = new Money();

			TenderReturn tenderReturn = new TenderReturn();
			tenderReturn.setStatus(21);
			tenderReturn.setLid(tender.getLid());
			tenderReturn.setUid(tender.getUid());

			tenderReturn.setTid(tender.getTid());
			tenderReturn.setIsFlow(isFlow);
			tenderReturn.setPrincipal(tender.getAmount());

			tenderReturn.setAmount(tempAllRepay);

			tenderReturn.setPrincipal(tender.getAmount());

			tenderReturn.setNo(1);
			tenderReturn.setReturnType(ReturnType.NORMAL.getCode());
			
			Date date = DateUtil.addMonth(now, loanInfo.getDeadline());
			tenderReturn.setTime(date);
			tenderReturn.setStartTime(now);
			list.add(tenderReturn);

		}
		return list;
	}

	@Override
	public void batchCreate(List<Tender> tenderList, LoanInfo loanInfo) {
		// TODO Auto-generated method stub

	}

	@Override
	public List<TenderReturn> queryByDate(Date date, Integer isFlow) {
		Date startTime = DateUtil.getTheDayStartTime(date);
		Date endTime = DateUtil.getTheDayEndTime(date);

		return this.tenderReturnDao.queryAllByTime(startTime, endTime, isFlow,TenderReturnStatus.RECYCLING.getCode());
	}


	@Override
	public boolean updateStatusForReturn(Long id, Integer returnType,
			Money extraInterest) {
		
		return 1 == tenderReturnDao.updateStatusForReturn(id, returnType, extraInterest);
	}

	public TenderReturnResult doReturnForAdvance(){
		TenderReturnResult result = new TenderReturnResult();
		//this.tenderReturnDao.
		
		
		result.setSuccess(true);
		return result;
	}
	
	public List<TransInfo> createTransInfo(TenderReturn tenderReturn){
		String userAccountNo = AccountUtil.getInvestorsAccountNoByUserId(String.valueOf(tenderReturn.getUid()));
		List<TransInfo> list = new ArrayList<TransInfo>();
		if(tenderReturn.getPrincipal().getCent()>0){
			
			
			TransInfo transInfo = new TransInfo();
			transInfo.setTransCode(TransCodeEnum.TXCODE_TRANSFER);
			transInfo.setSubTransCode(SubTransCodeEnum.SUB_TRANSFER_RETURN);
			transInfo.setTransAccountIn(userAccountNo);
			transInfo.setTransAccountOut(InnerAccountConsts.innerMiddleAccountNo);
			
			transInfo.setTransAmount(tenderReturn.getPrincipal());
			transInfo.setTransMemo("投资回款本金");
			list.add(transInfo);
		}
		if(tenderReturn.getAmount().getCent()>0){
			TransInfo transInfo = new TransInfo();
			transInfo.setTransCode(TransCodeEnum.TXCODE_TRANSFER);
			transInfo.setSubTransCode(SubTransCodeEnum.SUB_TRANSFER_RETURN);
			transInfo.setTransAccountIn(userAccountNo);
			transInfo.setTransAccountOut(InnerAccountConsts.innerMiddleAccountNo);
			Money transAmount = new Money(tenderReturn.getAmount().getCent()-tenderReturn.getPrincipal().getCent());
			transInfo.setTransAmount(transAmount);
			transInfo.setTransMemo("投资回款利息");
			list.add(transInfo);
		}
		if(tenderReturn.getExtraInterest().getCent()>0){
			TransInfo transInfo = new TransInfo();
			transInfo.setTransCode(TransCodeEnum.TXCODE_TRANSFER);
			transInfo.setSubTransCode(SubTransCodeEnum.SUB_TRANSFER_RETURN);
			transInfo.setTransAccountIn(userAccountNo);
			transInfo.setTransAccountOut(InnerAccountConsts.innerMiddleAccountNo);
			
			transInfo.setTransAmount(tenderReturn.getExtraInterest());
			transInfo.setTransMemo("投资回款逾期利息");
			list.add(transInfo);
		}
		
		return list;
	}
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public TenderReturnResult doReturn(TenderReturn tenderReturn,Boolean advance,LoanInfo loanInfo,Date now){
		
		TenderReturnResult result = new TenderReturnResult();
		String userAccountNo = AccountUtil.getInvestorsAccountNoByUserId(String.valueOf(tenderReturn.getUid()));
		
		
		//Money transAmount = new Money();
		
		/*TransInfo transInfo = new TransInfo();
		transInfo.setTransCode(TransCodeEnum.TXCODE_TRANSFER);
		transInfo.setSubTransCode(SubTransCodeEnum.SUB_TRANSFER_RETURN);
		transInfo.setTransAccountIn(userAccountNo);
		transInfo.setTransAccountOut(InnerAccountConsts.innerMiddleAccountNo);*/
		
		//非提前还款
		if(!advance){
			Date returnTime = tenderReturn.getTime();
			int days = DateUtil.getDayBetweenTwoDate(now.getTime(), returnTime.getTime());
			
			//逾期费用
			long dayInterestCent = 0l;
			if(days>0){
				//每日费率
				double dayRate = RateUtil.getDayliRate(loanInfo.getApr());
				//根据逾期天数计算费用
				dayInterestCent = (long)(tenderReturn.getAmount().getCent()*dayRate);
				
				//产生预期费用就是逾期还款
				tenderReturn.getExtraInterest().setCent(dayInterestCent);
				tenderReturn.setReturnType(ReturnType.OVERDUE.getCode());
			}
			
			/*transAmount.setCent(tenderReturn.getAmount().getCent()+dayInterestCent);
			transInfo.setTransAmount(transAmount);
			transInfo.setTransMemo("投资回款");*/
			
		} else {// 提前还款
			
			Date startTime = tenderReturn.getStartTime();
			int days = DateUtil.getDayBetweenTwoDate(
					now.getTime(), startTime.getTime());

			// 利息费用
			long dayInterestCent = 0l;
			if (days > 0) {
				// 每日费率
				double dayRate = RateUtil.getDayliRate(loanInfo.getApr());
				// 根据逾期天数计算费用
				dayInterestCent = (long) (tenderReturn.getAmount().getCent() * dayRate);

				// 产生利息费
				tenderReturn.getExtraInterest().setCent(dayInterestCent);
			}
			/*transAmount.setCent(tenderReturn.getPrincipal().getCent()
					+ dayInterestCent);
			transInfo.setTransAmount(transAmount);
			transInfo.setTransMemo("提前还款");*/

			// 设置状态为提前还款
			tenderReturn.setReturnType(ReturnType.ADVANCE.getCode());
		}
		
		boolean returnResult = this.updateStatusForReturn(tenderReturn.getId(), tenderReturn.getReturnType(), tenderReturn.getExtraInterest());
		
		if(!returnResult){
			
			result.setResultCode(TenderReturnResult.ResultCodeEnum.TenderReturn_status_error.name());
			
			return result;
		}
		
		if(isLastReturn(loanInfo,tenderReturn)){
			
			tenderService.updateTenderStatus(tenderReturn.getTid(), TenderStatus.REPAYED.getCode());
		}
		
		if(tenderReturn.getAmount().getCent()>=0l){
			List<TransInfo> transList = this.createTransInfo(tenderReturn);
			
			AccountTransResult transResult = accountTransFacade.batchTransfer(transList);
			if(transResult.isSuccess()){
				logger.info("tender return succes return id="+tenderReturn.getId());
				broadcastForTenderReturn(tenderReturn);
			}else{
				logger.error("tender return error return id="+tenderReturn.getId()+",transResult="+transResult);
				result.setResultCode(transResult.getResultCode());
				return result;
			}
			
		}
		/*result.setReturnPrincipal(tenderReturn.getPrincipal());
		result.setReturnAmount(transInfo.getTransAmount());*/
		result.setTenderReturn(tenderReturn);
		result.setSuccess(true);
		return result;
	}
	
	private void broadcastForTenderReturn(TenderReturn tenderReturn){
		ActiveMQTopic destination = new ActiveMQTopic("VirtualTopic.TEND_RETURN_COMPLETE");
		producerService.sendMessage(destination, tenderReturn);
	}
	
	private boolean isLastReturn(LoanInfo loanInfo,TenderReturn tenderReturn){
		String repayMethod = loanInfo.getRepayMethod();
		if(StringUtils.equals(repayMethod,LoanRepayMethod.e.name())){
			return true;
		}
		
		int deadline = loanInfo.getDeadline();
		if(deadline==tenderReturn.getNo().intValue()){
			return true;
		}else{
			return false;
		}
	}
	@Override
	public List<TenderReturn> queryByLidAndNo(Long lid, Integer no) {
		return tenderReturnDao.queryByLidAndNo(lid, no);
	}
	
	@Override
	public List<TenderReturn> queryByLidAndNoAndStatus(Long lid, Integer no,Integer status) {
		return tenderReturnDao.queryByLidAndNoAndStatus(lid, no, status);
	}

	@Override
	public PageVo listPage(PageVo form) {
		List<Map<String, Object>> list = tenderReturnDao.listPage(form);
		if (list != null && list.size()>0) {
			List<Map<String, Object>> loanList = loanInfoService
					.batchList(list);
			merge(list, loanList);			
			form.setList(list);
		}
		return form;
	}
	
	@Override
	public PageVo listPageRecycled(PageVo form) {
		List<Map<String, Object>> list = tenderReturnDao.listPageRecycled(form);
		if (list != null && list.size()>0) {
			List<Map<String, Object>> loanList = loanInfoService
					.batchList(list);
			merge(list, loanList);			
			form.setList(list);
		}
		return form;
	}
	
	@Override
	public PageVo canPublishListPage(Long uid,PageVo form) {
		List<Map<String, Object>> list = tenderReturnDao.listPage(form);
		//canpublish
		if (list != null && list.size()>0) {
			List<Map<String, Object>> loanList = loanInfoService
					.batchList(list);
			merge(list, loanList);
			pushCanPublishTag(uid, list);
			form.setList(list);
		}
		return form;
	}

	private void pushCanPublishTag(Long uid, List<Map<String, Object>> list) {
		for(Map<String,Object> map:list){
			Long tid= MapUtils.getLong(map, "tid");
			boolean canPublish=transferService.canTransferFast(uid, tid);
			if(canPublish){
				map.put("canPublish", true);
			}else{
				map.put("canPublish", false);
			}
		}
	}

	private Integer transfering(Map<String, Object> map,
			List<Map<String, Object>> transferingList) {
		for(Map<String,Object> transfering:transferingList){
			Object transferingTid = transfering.get("tid");
			Object tid = map.get("tid");
			if(transferingTid.equals(tid)){
				return 1;
			}
		}
		return 0;
	}

	public static void merge(List<Map<String, Object>> list,
			List<Map<String, Object>> loanList){
		merge(list, loanList,false);
	}
	//合并贷款数据到主表中
	//把2个集合中的数据合并到一个集合中，通过某个主键关联（不用数据库left join）
	//TODO 需要重构啊
	public static void merge(List<Map<String, Object>> list,
			List<Map<String, Object>> loanList,boolean addStatus) {
		int size = list.size();
		for (int index = 0; index < size; index++) {
			Map<String, Object> mainMap = list.get(index);
			Map<String, Object> loanMap = relationLoanMap(mainMap.get("lid"),
					loanList,"lid");
			if(mainMap == null || loanMap == null){
				continue;
			}
			//TODO 只保留一种格式，“下划线的”
			//TODO mainMap.putAll(loanMap);		
			mainMap.put("loanBeginTime", loanMap.get("begintime"));
			mainMap.put("loanLno", loanMap.get("lno"));
			mainMap.put("loanApr", loanMap.get("apr"));
			mainMap.put("loanUid", loanMap.get("uid"));
			mainMap.put("loanAmout", loanMap.get("amout"));
			
			mainMap.put("loan_deadline", loanMap.get("deadline"));
			mainMap.put("loan_progress", loanMap.get("progress"));
			mainMap.put("loan_img", loanMap.get("img"));
			mainMap.put("loan_uid", loanMap.get("uid"));
			mainMap.put("loan_title", loanMap.get("title"));
			mainMap.put("loan_apr", loanMap.get("apr"));
			mainMap.put("loan_is_flow", loanMap.get("is_flow"));
			mainMap.put("loan_lno", loanMap.get("lno"));
			//TODO
			if(addStatus){
				mainMap.put("loanStatus", loanMap.get("status"));
			}
			
		}
	}

	/**
	 * 从一个集合中找到目标对象，如果没有找到，返回null
	 */
	public static Map<String, Object> relationLoanMap(Object object,
			List<Map<String, Object>> list,String key) {
		for (Map<String, Object> map : list) {
			if (map.get(key).equals(object)) {
				return map;
			}
		}
		return null;
	}

	@Override
	public int batchDeleteByIdList(List<Long> idList) {
		return tenderReturnDao.batchDeleteByIdList(idList);
	}

	@Override
	public List<TenderReturn> queryByUserCondition(TenderReturn tenderReturn) {
		return tenderReturnDao.queryByUserCondition(tenderReturn);
	}

	@Override
	public long getEarnings(Date lastUpdateTime) {
		// TODO Auto-generated method stub
		return tenderReturnDao.getEarnings(lastUpdateTime);
	}

	@Override
	public Long getDayEarnings(Date dateBegin, Date dateEnd) {
		// TODO Auto-generated method stub
		return tenderReturnDao.getDayEarnings(dateBegin,dateEnd);
	}


	@Override
	public PageVo queryByTime(PageVo form, long beginTime, long endTime) {
		
		form.setStartDate(String.valueOf(beginTime));
		form.setEndDate(String.valueOf(endTime));
		List<TenderReturn> list =  tenderReturnDao.queryByTime(form);
		form.setList(list);
		return form;
	}

	@Override
	public boolean updateStatusFromReturn(Long id) {
		// TODO Auto-generated method stub
		return false;
	}

	public List<Map<String, Object>> listAll(Integer uid){
		return tenderReturnDao.listAll(uid);
	}

}
